package middleware

import (
	"context"
	"errors"
	"fmt"
	"log"

	"github.com/go-kit/kit/endpoint"
	pe "github.com/pkg/errors"
	"go.opentelemetry.io/otel"
	"go.uber.org/zap"
	"xincan.com.cn/tarot-go/internal/common/exception"
	"xincan.com.cn/tarot-go/internal/tarot"
)

// LoggerMiddleware
// Title    			LoggerMiddleware
// Description   		日志中间件
// Author      			alittlexincan@163.com       			2024/07/17 11:36
// Update    			alittlexincan@163.com       			2024/07/17 11:36
// Param    			logging *TraceLogger					日志实例
// Return    			endpoint.Middleware     				日志中间件
func LoggerMiddleware() endpoint.Middleware {
	return func(next endpoint.Endpoint) endpoint.Endpoint {
		return func(ctx context.Context, request interface{}) (response interface{}, err error) {
			// 从context中获取TraceID
			// 调用链中的下一个端点（这最终会调用您的业务逻辑和验证中间件）
			defer func() {
				// 判断response是否是error类型
				if resErr, ok := response.(error); ok {
					// 获取堆栈信息
					msg := stackTrace(ctx, resErr)
					log.Println(msg)
				}
				// 判断错误是否为空
				if err != nil {
					// 获取堆栈信息
					msg := stackTrace(ctx, err)
					log.Println(msg)
				}
			}()
			return next(ctx, request)
		}
	}
}

// stackTrace
// Title    			stackTrace
// Description   		获取堆栈信息
// Author      			alittlexincan@163.com       							2024/07/17 11:36
// Update    			alittlexincan@163.com       							2024/07/17 11:36
// Param    			ctx 					context.Context					上下文
// Param    			err 					error							错误
// Return    			string     												堆栈信息
func stackTrace(ctx context.Context, err error) string {
	// 获取堆栈信息
	stack := "无堆栈信息"
	// 运行时错误, 获取错误码、错误信息
	var runtime exception.RuntimeError
	if errors.As(err, &runtime) {
		stack = logErrorAndExtractStack(ctx, runtime.Message(), runtime.Unwrap())
	}

	// 业务错误, 获取错误码、错误信息
	var business exception.BusinessError
	if errors.As(err, &business) {
		var msg string
		if business.Model() != "" {
			msg = fmt.Sprintf("%s: %s", business.Model(), business.Message())
		} else {
			msg = business.Message()
		}
		stack = logErrorAndExtractStack(ctx, msg, business.Unwrap())
	}
	return stack
}

// logErrorAndExtractStack
// Title    			logErrorAndExtractStack
// Description   		记录错误并提取堆栈信息
// Author      			alittlexincan@163.com       							2024/07/17 11:36
// Update    			alittlexincan@163.com       							2024/07/17 11:36
// Param    			ctx 					context.Context					上下文
// Param    			message 				string							错误信息
// Param    			err 					error							错误
// Return    			string     												堆栈信息
func logErrorAndExtractStack(ctx context.Context, message string, err error) string {
	tarot.Logger.ContextLogger(ctx).Error(message, zap.Error(err))

	// 1: 获取OpenTelemetry信息, 创建新的Span
	tracer := otel.GetTracerProvider().Tracer("middleware")
	_, span := tracer.Start(ctx, "request-handler")
	defer span.End()
	spanCtx := span.SpanContext()

	// 如果错误为空，则返回错误信息
	if err == nil {
		return fmt.Sprintf("{\"traceId\": \"%s\",\"spanId\": \"%s\",\"message\":\"%s\"}", spanCtx.TraceID(), spanCtx.SpanID(), message)
	}
	// 错误不为空，则返回错误信息
	if stackTracer, ok := err.(interface{ StackTrace() pe.StackTrace }); ok {
		return fmt.Sprintf("{\"traceId\": \"%s\",\"spanId\": \"%s\",\"message\":\"%s\",\"err\":\"%s\"} %+v", spanCtx.TraceID(), spanCtx.SpanID(), message, err.Error(), stackTracer.StackTrace())
	} else {
		// 备用方案：直接使用 %+v（如果err是用pkg/errors包装的）
		return fmt.Sprintf("{\"traceId\": \"%s\",\"spanId\": \"%s\",\"message\":\"%s\",\"err\":\"%s\"}", spanCtx.TraceID(), spanCtx.SpanID(), message, err.Error())
	}
}
